package com.pine.dbant.common.plugin.meta.function.jdbc;

import com.pine.dbant.common.model.JdbcMetadataTreeView;
import com.pine.dbant.common.model.Table;
import com.pine.dbant.common.plugin.meta.*;
import com.pine.sunflower.core.utils.Strings;
import lombok.extern.slf4j.Slf4j;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

/**
 * @author xiaoyuan
 * @create 2022/12/30 11:18
 **/
@Slf4j
public class ViewsFunction extends AbstractQueryMetadataFunction<JdbcMetadataAnalysisConfig, List> {
    //    protected static final String TABLE_CAT = "TABLE_CAT";
    protected static final String TABLE_SCHEM = "TABLE_SCHEM";
    protected static final String TABLE_NAME = "TABLE_NAME";
    protected static final String TABLE_TYPE = "TABLE_TYPE";
    protected static final String REMARKS = "REMARKS";

    public ViewsFunction(String type) {
        super(type);
    }

    @Override
    public List<MetadataNode> doQuery(JdbcMetadataAnalysisConfig metadataAnalysisConfig, QueryMetadataParam queryMetadataParam) {
        DatabaseMetaData dbMetaData;
        try {
            dbMetaData = metadataAnalysisConfig.getConnection().getMetaData();
        } catch (SQLException e) {
            throw new JdbcMetaAnalysisException(e.getMessage(), e);
        }
        List<MetadataNode> metadataNodes = new ArrayList<>();
        String catalog = queryMetadataParam.getConfiguration().getNecessaryValue("catalog");
        List<Table> tables = buildTableInfo(dbMetaData, catalog, catalog);
        tables.forEach(table -> metadataNodes.add(new DefaultMetadataNode()
                .setName(table.getTableName())
                .setType(JdbcMetadataTreeView.TABLE.getType())
                .setId(Strings.format("{}_{}_{}",metadataAnalysisConfig.getConnectionInfo().getId(),catalog,table.getTableName()))
                .addProperty("connId", metadataAnalysisConfig.getConnectionInfo().getId())
                .addProperty("catalog", catalog)
                .addProperty("tableName", table.getTableName())));
        return metadataNodes;
    }

    public List<Table> buildTableInfo(DatabaseMetaData dbMetaData, String catalog, String schemaName) {
        List<Table> tables = new ArrayList<>();
        try (ResultSet rs = dbMetaData.getTables(catalog, schemaName, null,
                new String[]{"VIEW"})) {
            while (rs.next()) {
                Map<String, Object> map = map(rs);
//                String tbCatalog = rs.getString(TABLE_CAT);
                String tbSchema = rs.getString(TABLE_SCHEM);
                String tbName = rs.getString(TABLE_NAME);
                // dbMetaData.getTables(catalog, schemaPattern, tablePattern)中：
                // schemaPattern, tablePattern表示模糊匹配
                // Jdk Doc: 一些DatabaseMetaData方法使用String模式的参数。 这些参数都有名称，如fooPattern。
                // 在模式字符串中，“%”表示匹配0个或更多字符的任何子字符串，“_”表示匹配任何一个字符。
                // 只返回与搜索模式匹配的元数据条目。 如果搜索模式参数设置为null ，则该参数的标准将从搜索中删除。
                // 这种情况可能会查出其他schema或者其他表, 需要过滤
                if (schemaName != null && !schemaName.equals(tbSchema)) {
                    continue;
                }
                // 表类型
                String tableType = rs.getString(TABLE_TYPE);
                // 表备注
                String remarks = rs.getString(REMARKS);
                Table table = new Table();
                table.setCatalogName(catalog);
                table.setSchemaName(schemaName);
                table.setTableComment(remarks);
                table.setTableName(tbName.trim());
                table.setTableType(tableType);
                tables.add(table);
            }
        } catch (SQLException e) {
            log.error("获取数据库[{}]->schema[{}]下的表信息错误", catalog, schemaName, e);
            throw new JdbcMetaAnalysisException(String.format("获取数据库[%s]->schema[%s]下的表信息错误", catalog, schemaName), e);
        }
        return tables;
    }

    public Map<String, Object> map(ResultSet row) throws SQLException {
        Map<String, Object> map = new Hashtable<>();
        ResultSetMetaData metaData = row.getMetaData();
        int count = metaData.getColumnCount();
        for (int i = 1; i <= count; i++) {
            String key = metaData.getColumnName(i);
            Object value = row.getObject(i);
            map.put(key, value == null ? "" : value);
        }
        return map;
    }
}
